package testApplets;

import java.awt.geom.Line2D;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Vector;

import marchingCubes.IsMCVertex;
import marchingCubes.MCube;
import marchingCubes.MCubeEngine;
import marchingCubes.MCubeEngineLowMemory;
import marchingCubes.MarchinCube;
import meshToSolid.STLWriter;

import peasy.PeasyCam;
import processing.core.PApplet;
import processing.core.PMatrix3D;
import simpleGeom.Bounds3D;
import simpleGeom.Box3D;
import simpleGeom.Face3D;
import simpleGeom.HasPos3D;
import simpleGeom.Line3D;
import simpleGeom.SimpleTriangle;
import simpleGeom.Vec3D;
import testApplets.MeshToSolid2.Cell;

public class LinesToSolidApplet extends PApplet{
	Vector<SimpleTriangle> meFaces;
	Vector<Face3D> meFaces2;
public void setup(){
	size(1024,768,P3D);
	this.initdata();
	//load(this.selectInput());
	createGrid();
	calculateDistances();
	
	float[][][] dist=new float[nX][nY][nZ];
	for (int x = 0; x < nX; x++) {
		for (int y = 0; y < nY; y++) {
			for (int z = 0; z < nZ; z++) {
				dist[x][y][z]=grid[x][y][z].getValue();
				
			}
		}
	}
	//int nX, int nY, int nZ,float iso,float isoMin,float isoMax,double[][][] values
	//MCubeEngine mc=new MCubeEngine(nX,nY,nZ);
	//mc.setValues(dist);
	//mc.setIso(iso);
	//mc.calculate();
	//meFaces2=mc.getFaces();
	//println(meFaces2.size());
	meFaces=MarchinCube.polygonalise(dist, iso);
	println(meFaces.size());
	//meFaces = MCubeEngineLowMemory.initCubesTest(grid, iso, null);
}
ArrayList<Vec3D>nodes=new ArrayList<Vec3D>();
Vector<Spring>springs=new Vector<Spring>();
Box3D bounds3d;
int nX;
int nY;
int nZ;
int nYZ;
float sX,sY,sZ;

float dim=0.47f;//0.3f
Cell[][][] grid;
float iso=1f;//0.8f
public void createGrid() {
	println(bounds3d);
	nX = (int) (bounds3d.getDimX() / dim);
	nY = (int) (bounds3d.getDimY() / dim);
	nZ = (int) (bounds3d.getDimZ() / dim);
	nYZ = nY * nZ;
	sX = bounds3d.getX1();
	sY = bounds3d.getY1();
	sZ = bounds3d.getZ1();
	grid = new Cell[nX][nY][nZ];
	println(nX + " " + nY + " " + nZ);
	int id = 0;
	for (int x = 0; x < nX; x++) {
		println("x: " + x + "/" + nX);
		for (int y = 0; y < nY; y++) {
			for (int z = 0; z < nZ; z++) {
				float x1 = x * dim + sX;
				float y1 = y * dim + sY;
				float z1 = z * dim + sZ;
				Cell cell = new Cell(x1, y1, z1);
				cell.id = id;
				id++;
				cell.setValue(1000);
				grid[x][y][z] = cell;
			}
		}
	}
}
float minStress=Float.MAX_VALUE;
float maxStress=Float.NEGATIVE_INFINITY;
float minL=Float.MAX_VALUE;
float maxL=Float.NEGATIVE_INFINITY;
public void initdata(){
	springs=new Vector<Spring>();
	Spring spring=new Spring(-10,-10,-10,10,10,10);
	springs.add(spring);
	this.bounds3d=new Box3D(-10,-10,-10,10,10,10);
	PeasyCam cam=new PeasyCam(this,bounds3d.getMaxDimension());
}

public void load(String file){
	String[] strings= this.loadStrings(file);
	nodes=new ArrayList<Vec3D>();
	for (int i=0;i<strings.length;i++){
		String s=strings[i];
		String[] chars=s.split(" ");
		if (chars[0].equals("v")){
			float x=Float.valueOf(chars[1]);
			float y=Float.valueOf(chars[2]);
			float z=Float.valueOf(chars[3]);
			nodes.add(new Vec3D(x,y,z));
		}
	}
	
	Bounds3D bounds3d=new Bounds3D();
	bounds3d.addAll(nodes);
	float pX=bounds3d.getCenterX();
	float pY=bounds3d.getCenterY();
	float pZ=bounds3d.getZ1();
	float scaleX=80f/bounds3d.getDimX();
	float scaleY=140f/bounds3d.getDimY();
	float scaleZ=90f/bounds3d.getDimZ();
	for (int i=0;i<nodes.size();i++){
		Vec3D node=nodes.get(i);
		node.setX((node.getX()-pX)*scaleX);
		node.setY((node.getY()-pY)*scaleY);
		node.setZ((node.getZ()-pZ)*scaleZ);
	}
	for (int i=0;i<nodes.size();i++){
		Vec3D node=nodes.get(i);
		float dY=abs(node.x);
		if (dY>10&&node.z>60){
			node.setZ(node.getZ()*0.9f);
		}
	}
	bounds3d=new Bounds3D();
	bounds3d.addAll(nodes);
	println(bounds3d);
	this.bounds3d=bounds3d.getOffsetBox(iso*4);
	println(this.bounds3d);
	springs=new Vector<Spring>();
	for (int i=0;i<strings.length;i++){
		String s=strings[i];
		String[] chars=s.split(" ");
		if (chars[0].equals("l")){
			int id1=Integer.valueOf(chars[1]);
			Vec3D n1=nodes.get(id1-1);
			
			int id2=Integer.valueOf(chars[2]);
			Vec3D n2=nodes.get(id2-1);
			String comment=chars[3];
			comment= comment.substring(1);
			float stress=Float.valueOf(comment);
			if (stress<minStress){
				minStress=stress;
			}
			if (stress>maxStress){
				maxStress=stress;
			}
			Spring spring=new Spring(n1.x,n1.y,n1.z,n2.x,n2.y,n2.z);
			float cL=spring.length();
			if (cL<minL){
				minL=cL;
			}
			if (cL>maxL){
				maxL=cL;
			}
			spring.stress=stress;
			springs.add(spring);
		}
	}
	
	PeasyCam cam=new PeasyCam(this,900);
}
public void draw(){
	if (flagSave){
		saveObjPrintwriter(this.selectOutput());
		flagSave=false;
	}
	background(0,0,255);
	lights();
	noStroke();
	scale(4);
	for (int i=0;i<springs.size();i++){
		Spring spring=springs.get(i);
		if (spring.stress<0){
			//stroke(255,0,0);
		}
		else{
			//stroke(0,255,0);
		}
		line(spring.x1,spring.y1,spring.z1,spring.x2,spring.y2,spring.z2);
	}
	translate(-nX*0.5f,-nY*0.5f,-nZ*0.5f);
	if (meFaces != null) {
		for (int i = 0; i < meFaces.size(); i++) {
			SimpleTriangle f = meFaces.get(i);
			f.display3D(g);
		}
	}
	
	//scale(10);
	
	//noFill();
	//stroke(255,0,0);
	if (meFaces2 != null) {
		for (int i = 0; i < meFaces2.size(); i++) {
			Face3D f = meFaces2.get(i);
			//f.display3D(g);
		}
	}
}
public int xK(float x) {
	return Math.round((x - sX) / dim);
}

public int yK(float y) {
	return Math.round((y - sY) / dim);
}

public int zK(float z) {
	return Math.round((z - sZ) / dim);
}
public void saveSTL(String file) {
	STLWriter writer = new STLWriter();
	writer.beginSave(file + ".stl", meFaces.size());
	for (int i = 0; i < meFaces.size(); i++) {
		SimpleTriangle f = meFaces.get(i);
		writer.face(f.getNode(0), f.getNode(1), f.getNode(2));
	}
	writer.endSave();
}
public float getRound(float v,float dec){
	return Math.round(v*dec)/dec;
}
public void saveObjPrintwriter(String file) {
	PrintWriter output = createWriter(file);

	int idV = 1;

	for (int i = 0; i < meFaces.size(); i++) {
		SimpleTriangle f = meFaces.get(i);
		for (int j = 0; j < f.getNodesNum(); j++) {
			Vec3D n = (Vec3D) f.getNode(j);
			if (n.getId() == 0) {
				n.setId(idV);
				idV++;
				output.println("v " + getRound(n.x,1000) + " " + getRound(n.y,1000) + " " +  getRound(n.z,1000));
			}
		}
	}

	for (int i = 0; i < meFaces.size(); i++) {
		SimpleTriangle f = meFaces.get(i);
		int[] ids = new int[f.getNodesNum()];
		String string = "f";
		
		for (int j = 0; j < f.getNodesNum(); j++) {
			Vec3D n = (Vec3D) f.getNode(j);
			string += " " + n.getId();
		}
		output.println(string);
		
	}
	
	output.flush(); // Writes the remaining data to the file
	output.close();
}

public void calculateDistances() {

	// Box3D box3D = b3d.getOffsetBox(dim*4f+iso);
	println("maxStress"+maxStress);
	println("minStress"+minStress);
	println("maxL"+maxL);
	println("minL"+minL);
	for (int i = 0; i < springs.size(); i++) {
		
		Spring f = (Spring) springs.get(i);
		minL=2;
		maxL=20;
		//println(minL);
		float cStress=min(0,f.stress);
		//cStress=f.stress;
		float cL=min(maxL,f.length());
		 cL=max(minL,cL);
		//float cL=f.length();
		//float fac=(cStress-minStress)/(maxStress-minStress);
		//float fac=map(cL,minL,maxL,1.8f,1f);
		 float minZ=min(f.z1,f.z2);
		float fac=map(minZ,90,0,1.7f,1f);
		if (fac!=1){
		println(fac);
		}
		
		//float fac=map(cStress,minStress,0,2f,1f);
		Box3D faceBounds = f.getBounds3D();
		faceBounds = faceBounds.getOffsetBox(iso+2*dim);
		int x1 = xK(faceBounds.x1) - 1;
		int y1 = yK(faceBounds.y1) - 1;
		int z1 = zK(faceBounds.z1) - 1;
		x1 = max(x1, 0);
		y1 = max(y1, 0);
		z1 = max(z1, 0);
		int x2 = xK(faceBounds.x2) + 1;
		int y2 = yK(faceBounds.y2) + 1;
		int z2 = zK(faceBounds.z2) + 1;
		x2 = min(x2, nX);
		y2 = min(y2, nY);
		z2 = min(z2, nZ);
		for (int x = x1; x < x2; x++) {
			for (int y = y1; y < y2; y++) {
				for (int z = z1; z < z2; z++) {
					// float minD = Float.MAX_VALUE;
					Cell cell = grid[x][y][z];
					float cellX = cell.getX();
					float cellY = cell.getY();
					float cellZ = cell.getZ();
					if (faceBounds.isOverlap(cellX - dim, cellY - dim,
							cellZ - dim, cellX + dim, cellY + dim, cellZ
									+ dim)) {
						float d=sqrt(f.ptSegDistSq(cellX, cellY, cellZ));
						d*=fac;
						if (d<cell.getValue()){
							cell.setValue(d);
						}
					}
				}
			}
		}
	
	}
	int minP=8;
	int maxP=12;
	for (int x = 0; x < nX; x++) {
		for (int y = 0; y < nY; y++) {
			for (int z = minP; z < maxP; z++) {
				Cell cell = grid[x][y][z];
				cell.setValue(0);
			
			}
		}
	}
	for (int x = 0; x < nX; x++) {
		for (int y = 0; y < nY; y++) {
				Cell cell = grid[x][y][maxP];
				if (cell.getValue()>iso){
					//cell.setValue(1000);
				}
		}
	}
	for (int x = 0; x < nX; x++) {
		for (int y = 0; y < nY; y++) {
			for (int z = 0; z < nZ; z++) {
				if (x==0||x==nX-1||y==0||y==nY-1||z<minP||z==nZ-1){
					Cell cell = grid[x][y][z];
					cell.setValue(1000);
				}
			}
		}
	}
}
boolean flagSave;
public void keyPressed(){
	if (key=='s'){
		flagSave=true;
	}
}
class Spring extends Line3D{
	float stress;
	public Spring(float x1, float y1, float z1, float x2, float y2, float z2) {
		super(x1, y1, z1, x2, y2, z2);
		// TODO Auto-generated constructor stub
	}
	
}

public class Cell implements IsMCVertex {
	
	int id;
	float d = Float.MAX_VALUE;
	boolean inside;
	MCube cube;

	public Cell() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Cell(float x, float y, float z) {
		/*
		 * this.x=x; this.y=y; this.z=z;
		 */
	}

	public Cell(int id) {
		this.id = id;
	}

	public float getValue() {
		// TODO Auto-generated method stub
		return d;
	}

	public void setValue(double value) {
		// TODO Auto-generated method stub
		this.d = (float) value;
	}

	public MCube getCube() {
		// TODO Auto-generated method stub
		return cube;
	}

	@Override
	public float getX() {
		// TODO Auto-generated method stub
		// return nX%3;
		return (int) (id * 1f / (nYZ)) * dim + sX;
		// return x;
	}

	@Override
	public float getY() {
		// TODO Auto-generated method stub
		return ((int) (id / nZ)) % nY * dim + sY;
		// return y;
	}

	public float getZ() {
		// TODO Auto-generated method stub
		return id % nZ * dim + sZ;
		// return z;
	}

	public int getXK() {
		// TODO Auto-generated method stub
		// return nX%3;
		return (int) (id * 1f / (nYZ));
		// return x;
	}

	public int getYK() {
		// TODO Auto-generated method stub
		return ((int) (id / nZ)) % nY;
		// return y;
	}

	public int getZK() {
		// TODO Auto-generated method stub
		return id % nZ;
		// return z;
	}

	public void setCube(MCube cube) {
		// TODO Auto-generated method stub
		this.cube = cube;
	}

	@Override
	public void setPosition(float x, float y, float z) {
		// TODO Auto-generated method stub

	}

	@Override
	public void setZ(float z) {
		// TODO Auto-generated method stub
		// this.z=z;
	}

	@Override
	public void setPosition(float x, float y) {
		// TODO Auto-generated method stub

	}

	@Override
	public void setX(float x) {
		// TODO Auto-generated method stub

	}

	@Override
	public void setY(float Y) {
		// TODO Auto-generated method stub

	}

	@Override
	public int getId() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void setId(int i) {
		// TODO Auto-generated method stub

	}
}
}
